package com.zhoug.chart3.charts;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;

import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.components.IMarker;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
import com.zhoug.chart3.core.styleconfig.IMPPieStyleConfig;
import com.zhoug.chart3.core.styleconfig.MPPieStyleConfig;
import com.zhoug.chart3.data.HkChartData;
import com.zhoug.chart3.data.MPPieChartDataCreator;
import com.zhoug.chart3.data.OnDateSetCreateListener;
import com.zhoug.chart3.formatters.MPAxisTextValueFormatter;
import com.zhoug.chart3.formatters.MPPieDataSetValueFormatter;
import com.zhoug.chart3.ints.IMPChartPlaceholder;
import com.zhoug.chart3.renderer.MPPieChartRenderer;

import java.util.List;

import androidx.annotation.NonNull;

/**
 * 饼图
 *
 * @Author: zhoug
 * @Date: 2024-01-30
 * @Description:
 */
public class MPPieChart extends PieChart implements IMPChart<PieData, IMPPieStyleConfig> {
    protected MPChartHelper mChartHelper;
    protected IMPPieStyleConfig mStyleConfig;
    protected String mKey;
    protected OnDateSetCreateListener<PieDataSet> mOnDateSetCreateListener;
    protected ValueFormatter mDataSetValueFormatter;

    /**
     * array that holds the width of each pie-slice in degrees
     */
    protected float[] mDrawAngles = new float[1];
    /**
     * array that holds the absolute angle in degrees of each slice
     */
    protected float[] mAbsoluteAngles = new float[1];
    /**
     * Minimum angle to draw slices, this only works if there is enough room for all slices to have
     * the minimum angle, default 0f.
     */
    protected float mMinAngleForSlices = 0f;

    public MPPieChart(Context context) {
        this(context, null);
    }

    public MPPieChart(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MPPieChart(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mChartHelper = new MPChartHelper(this, context, attrs, defStyle);
        mStyleConfig = new MPPieStyleConfig();
        mDataSetValueFormatter = new MPPieDataSetValueFormatter(this);

        setAnimateDirection(ANIM_DIRECTION_Y);
        MPPieChartRenderer renderer = new MPPieChartRenderer(this, getAnimator(), getViewPortHandler());
        setRenderer(renderer);
    }

    @Override
    public void setData(PieData data) {
        if (data != null && getMarker() != null) {
            highlightValue(null);
        }
        super.setData(data);
    }


    @Override
    public void setData1(PieData data) {
        if (data != null) {
            setData(data);
            notifyDataSetChanged();
            postInvalidate();
            mChartHelper.animate();
        } else {
            empty();
        }
    }


    /**
     * 设置数据
     *
     * @param list
     */
    @Override
    public void setHkChartData(List<HkChartData> list) {
        MPPieChartDataCreator creator = new MPPieChartDataCreator(mStyleConfig);
        creator.setDataSetValueFormatter(mDataSetValueFormatter);
        creator.setOnDateSetCreateListener(mOnDateSetCreateListener);
        PieData data = creator.cratePieData(list, mKey);
        setData1(data);
    }

    public void setKey(String key) {
        this.mKey = key;
    }

    public void setDataSetValueFormatter(ValueFormatter dataSetValueFormatter) {
        this.mDataSetValueFormatter = dataSetValueFormatter;
    }

    public void setOnDateSetCreateListener(OnDateSetCreateListener<PieDataSet> onDateSetCreateListener) {
        this.mOnDateSetCreateListener = onDateSetCreateListener;
    }

    /**
     * 绘制value时占比大于此值的value才绘制
     *
     * @param minValueCanDraw
     */
    public void setMinValuePercentCanDraw(float minValueCanDraw) {
       if(mRenderer instanceof MPPieChartRenderer){
           ((MPPieChartRenderer) mRenderer).setMinValuePercentCanDraw(minValueCanDraw);
       }
    }

    @Override
    public void setStyleConfig(@NonNull IMPPieStyleConfig styleConfig) {
        this.mStyleConfig = styleConfig;
    }

    @NonNull
    @Override
    public IMPPieStyleConfig getStyleConfig() {
        return this.mStyleConfig;
    }

    /**
     * 获取x轴数据格式化器
     *
     * @return
     */
    @Override
    public MPAxisTextValueFormatter getAxisTextValueFormatter() {
        return mChartHelper.getAxisTextValueFormatter();
    }


    /**
     * x轴开始的最小值
     *
     * @return
     */
    @Override
    public int getStartXValue() {
        return mChartHelper.getStartXValue();
    }

    @Override
    public void setChartPlaceholder(IMPChartPlaceholder chartPlaceholder) {
        mChartHelper.setChartPlaceholder(chartPlaceholder);
    }

    /**
     * 暂无数据
     */
    @Override
    public void empty() {
        mChartHelper.empty();
    }

    /**
     * 数据加载失败
     */
    @Override
    public void error() {
        mChartHelper.error();
    }

    /**
     * 重置占位图
     */
    @Override
    public void resetPlaceholder() {
        mChartHelper.resetPlaceholder();
    }

    /**
     * 占位图是否启用
     *
     * @param enable
     * @return
     */
    @Override
    public void setPlaceholderEnable(boolean enable) {
        mChartHelper.setPlaceholderEnable(enable);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        try {
            super.onDraw(canvas);
            mChartHelper.drawPlaceImage(canvas);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        mChartHelper.onLayout();
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mChartHelper.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    /**
     * 不缩放,x,y的scale重置为1
     */
    @Deprecated
    @Override
    public void zoomNotScale() {
        mChartHelper.zoomNotScale();
    }

    @Deprecated
    @Override
    public void zoom(float scaleX, float scaleY, float x, float y) {

    }

    /***
     * {@link #ANIM_DIRECTION_X,#ANIM_DIRECTION_Y,#ANIM_DIRECTION_XY}
     * @param animDirection
     */
    @Override
    public void setAnimateDirection(int animDirection) {
        mChartHelper.setAnimateDirection(animDirection);
    }

    /**
     * {@link #ANIM_DIRECTION_X,#ANIM_DIRECTION_Y,#ANIM_DIRECTION_XY}
     */
    @Override
    public int getAnimateDirection() {
        return mChartHelper.getAnimateDirection();
    }

    @Override
    public void setMarker(IMarker marker) {
        super.setMarker(marker);
        mChartHelper.setMarker(marker);
    }


    @Override
    protected void calcMinMax() {
        calcAngles();
    }

    /**
     * calculates the needed angles for the chart slices
     */
    protected void calcAngles() {

        int entryCount = mData.getEntryCount();

        if (mDrawAngles.length != entryCount) {
            mDrawAngles = new float[entryCount];
        } else {
            for (int i = 0; i < entryCount; i++) {
                mDrawAngles[i] = 0;
            }
        }
        if (mAbsoluteAngles.length != entryCount) {
            mAbsoluteAngles = new float[entryCount];
        } else {
            for (int i = 0; i < entryCount; i++) {
                mAbsoluteAngles[i] = 0;
            }
        }

        float yValueSum = mData.getYValueSum();

        List<IPieDataSet> dataSets = mData.getDataSets();

        boolean hasMinAngle = mMinAngleForSlices != 0f && entryCount * mMinAngleForSlices <= mMaxAngle;
        float[] minAngles = new float[entryCount];

        int cnt = 0;
        float offset = 0f;
        float diff = 0f;

        for (int i = 0; i < mData.getDataSetCount(); i++) {

            IPieDataSet set = dataSets.get(i);

            for (int j = 0; j < set.getEntryCount(); j++) {

                float drawAngle = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum);

                if (hasMinAngle) {
                    float temp = drawAngle - mMinAngleForSlices;
                    if (temp <= 0) {
                        minAngles[cnt] = mMinAngleForSlices;
                        offset += -temp;
                    } else {
                        minAngles[cnt] = drawAngle;
                        diff += temp;
                    }
                }

                mDrawAngles[cnt] = drawAngle;

                if (cnt == 0) {
                    mAbsoluteAngles[cnt] = mDrawAngles[cnt];
                } else {
                    mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt];
                }

                cnt++;
            }
        }

        if (hasMinAngle) {
            // Correct bigger slices by relatively reducing their angles based on the total angle needed to subtract
            // This requires that `entryCount * mMinAngleForSlices <= mMaxAngle` be true to properly work!
            for (int i = 0; i < entryCount; i++) {
                minAngles[i] -= (minAngles[i] - mMinAngleForSlices) / diff * offset;
                if (i == 0) {
                    mAbsoluteAngles[0] = minAngles[0];
                } else {
                    mAbsoluteAngles[i] = mAbsoluteAngles[i - 1] + minAngles[i];
                }
            }

            mDrawAngles = minAngles;
        }
    }

    /**
     * calculates the needed angle for a given value
     *
     * @param value
     * @param yValueSum
     * @return
     */
    protected float calcAngle(float value, float yValueSum) {
        if (yValueSum == 0) {
            PieData data = getData();
            if (data != null && data.getEntryCount() > 0) {
                return 1.0f / data.getEntryCount() * mMaxAngle;
            }
            return 0;
        }
        return value / yValueSum * mMaxAngle;
    }


    @Override
    protected float[] getMarkerPosition(Highlight highlight) {

        MPPointF center = getCenterCircleBox();
        float r = getRadius();

        float off = r / 10f * 3.6f;

        if (isDrawHoleEnabled()) {
            off = (r - (r / 100f * getHoleRadius())) / 2f;
        }

        r -= off; // offset to keep things inside the chart

        float rotationAngle = getRotationAngle();

        int entryIndex = (int) highlight.getX();

        // offset needed to center the drawn text in the slice
        float offset = mDrawAngles[entryIndex] / 2;

        // calculate the text position
        float x = (float) (r
                * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
                * mAnimator.getPhaseY())) + center.x);
        float y = (float) (r
                * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
                * mAnimator.getPhaseY())) + center.y);

        MPPointF.recycleInstance(center);
        return new float[]{x, y};
    }

    /**
     * returns an integer array of all the different angles the chart slices
     * have the angles in the returned array determine how much space (of 360°)
     * each slice takes
     *
     * @return
     */
    @Override
    public float[] getDrawAngles() {
        return mDrawAngles;
    }

    /**
     * returns the absolute angles of the different chart slices (where the
     * slices end)
     *
     * @return
     */
    @Override
    public float[] getAbsoluteAngles() {
        return mAbsoluteAngles;
    }

    /**
     * The minimum angle slices on the chart are rendered with, default is 0f.
     *
     * @return minimum angle for slices
     */
    public float getMinAngleForSlices() {
        return mMinAngleForSlices;
    }


    /**
     * Set the angle to set minimum size for slices, you must call {@link #notifyDataSetChanged()}
     * and {@link #invalidate()} when changing this, only works if there is enough room for all
     * slices to have the minimum angle.
     *
     * @param minAngle minimum 0, maximum is half of {@link #setMaxAngle(float)}
     */
    public void setMinAngleForSlices(float minAngle) {

        if (minAngle > (mMaxAngle / 2f))
            minAngle = mMaxAngle / 2f;
        else if (minAngle < 0)
            minAngle = 0f;

        this.mMinAngleForSlices = minAngle;
    }

    @Override
    public int getIndexForAngle(float angle) {
        // take the current angle of the chart into consideration
        float a = Utils.getNormalizedAngle(angle - getRotationAngle());
        for (int i = 0; i < mAbsoluteAngles.length; i++) {
            if (mAbsoluteAngles[i] > a)
                return i;
        }
        return -1; // return -1 if no index found
    }

}
